#ifndef _COMM_HPP_ // 防止头文件的重复包含
#define _COMM_HPP_

#include <iostream>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <sys/ipc.h>
#include <sys/shm.h>

#define PATHNAME "."
#define PROJ_ID 0x66
// 共享内存的大小，一般建议是4KB的整数倍
// 系统分配共享内存是以4KB为单位的！ --- 内存划分内存块的基本单位Page
#define MAX_SIZE 4097 // --- 内核给你的会向上取整， 内核给你的，和你能用的，是两码事
// 上面的 MAx_SIZE == 4097,因为得是 4kb 的整数倍，所以 os 给你开的大小是 2 * 4kb

key_t getKey()
{
    key_t k = ftok(PATHNAME, PROJ_ID); // 调用函数得到 k, 可以获取同样的一个Key！
    if (k < 0)
    {
        // 如果失败
        // cin, cout, cerr -> stdin, stdout, stderr -> 0, 1, 2
        // std::cerr  打印错误信息
        std::cerr << errno << ":" << strerror(errno) << std::endl;
        exit(1);
    }

    return k;
}

int getShmHelper(key_t k, int flags)
{ // k 和 flags，shmget() 函数里面的参数部分需要用的哦
    int shmid = shmget(k, MAX_SIZE, flags);
    if (shmid < 0)
    {
        std::cerr << errno << ":" << strerror(errno) << std::endl;
        exit(2);
    }
    return shmid;
}

int getShm(key_t k)
{
    return getShmHelper(k, IPC_CREAT); // getShm(key_t k) 的话，flags = IPC_CREAT
}

int createShm(key_t k)
{
    return getShmHelper(k, IPC_CREAT | IPC_EXCL | 0600);
    // 后面的 0600 相对于粘滞位的意思，队共享内存的使用权限的设置
}

// 链接共享内存(返回共享内存的地址)
void *attachShm(int shmid)
{
    // int a = 10;
    // 100u; // 字面值
    // 纯数字是没有意义的，必须得有类型才可以
    // 10u;
    // 10L;
    // 10;
    // 3.14f;
    // 100元，200$

    // shmat() 将共享内存段连接到进程地址空间
    void *mem = shmat(shmid, nullptr, 0); // Linux 默认是 64位系统的哦
    if ((long long)mem == -1L)
    { // 如果失败的情况
        std::cerr << "shmat: " << errno << ":" << strerror(errno) << std::endl;
        exit(3);
    }
    return mem;
}

// 去链接
void detachShm(void *start)
{
    // shmdt(void* start)   将共享内存段与当前进程脱离
    if (shmdt(start) == -1)
    {
        std::cerr << "shmdt: " << errno << ":" << strerror(errno) << std::endl;
    }
}

// 删除共享内存
void delShm(int shmid)
{
    // shmctl(int shmid)   用于控制共享内存
    if (shmctl(shmid, IPC_RMID, nullptr) == -1)
    {
        std::cerr << "shmctl: " << errno << ":" << strerror(errno) << std::endl;
    }
}

#endif
